// ​副作用是相对于主作用来说的，一个函数除了主作用，其他的作用就是副作用。
// 对于 React 组件来说，主作用就是根据数据（state/props）渲染 UI，除此之外都是副作用（比如，手动修改 DOM,修改状态（全局变量））
// 常见的副作用
// 1. 数据请求 ajax发送
// 2. 手动修改dom
// 3. localstorage操作
// useEffect函数的作用就是为react函数组件提供副作用处理的！

//useEffect回调 是在dom渲染《《之后》》执行！！！！
import { useState, useEffect } from "react";

function App() {
    const [count, setCount] = useState(0);
    const [list, setList] = useState([1, 2]);
    const [showText, setShowText] = useState(true);

    // 1. 不添加依赖项（默认状态）
    // > 组件首次渲染执行一次，以及不管是哪个状态更改引起组件更新时都会重新执行
    // > 1. 组件初始渲染
    // > 2. 组件更新 （不管是哪个状态引起的更新）
    useEffect(() => {
        // dom操作
        document.title = `当前已点击了${count}次`;
        console.log("没有添加依赖项的副作用执行了");
    });

    // 2. 添加空数组
    // > 组件只在首次渲染时执行一次
    // > 可以在这里发送请求!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    useEffect(() => {
        console.log("添加空数组依赖性的副作用执行了");
        function loadData() {
            fetch("http://geek.itheima.net/v1_0/channels")
                .then((res) => res.json())
                .then((data) => console.log(data));
        }
        loadData();
    }, []);

    // 3.添加特定依赖项
    // > 副作用函数在首次渲染时执行，在依赖项发生变化时重新执行,这里依赖项是list
    // 修改list时执行，修改count时不执行
    // useEffect 回调函数中用到的数据（比如:list）就是依赖数据，就应该出现在依赖项数组中，如果不添加依赖项就会有bug出现
    useEffect(() => {
        console.log("添加特定依赖项list的副作用执行了--》", list);
    }, [list]);

    const setData = () => {
        setCount(count + 1);
    };

    const setIsList = () => {
        setList([...list, count]);
    };

    return (
        <div>
            <button onClick={setData}>修改count状态</button>
            <button onClick={setIsList}>修改List状态</button>
            <hr />
            <span>{count}</span>
            <hr />
            <button onClick={() => setShowText(!showText)}>
                显示/隐藏Text组件
            </button>
            {showText ? <Text></Text> : ""}
        </div>
    );
}
function Text() {
    // 4. return一个清除副作用的函数
    // 使用useEffect清除副作用，只需在useEffect里return一个函数，在该函数里书写清除副作用的代码
    // 若不这样做，当组件卸载时副作用会一直存在
    // useEffect return的这个函数类似于类组件的卸载阶段的生命周期钩子
    useEffect(() => {
        let timer = setInterval(() => {
            console.log("Text组件定时器执行了");
        }, 1000);
        //return的这个函数只会在组件卸载时执行(前提是无依赖项)，如果有依赖项，则在依赖更新时也会执行
        return () => {
            // 在这里写清理副作用的代码
            console.log(
                "+++++++++++++useEffect清除副作用开始+++++++++++++++++"
            );
            clearInterval(timer);
        };
    });

    return <div>我是text</div>;
}
export default App;

/**
 * useLayoutEffect
 * 其 API 与 useEffect 相同，不同的是调用时机不同
 * useLayoutEffect和原来componentDidMount、componentDidUpdate一致，在react完成DOM更新后（还未开始渲染）马上同步调用，会阻塞页面渲染（同步）。
 * 而useEffect是会在整个页面渲染完才会调用，不会阻塞浏览器更新屏幕，这让你的应用看起来响应更快（异步）。
 *
 * 在实际使用时如果想避免页面抖动（在useEffect里修改DOM很可能出现，因为useEffect是在页面渲染后执行，在它里修改DOM又会触发一次页面的重新渲染）的话，可以把需要操作DOM的代码放在useLayoutEffect里，
 * 在这里做的DOM操作，会和react做出的更改一起被一次性渲染到屏幕上，只有一次回流，重绘的代价。
 *
 * 官方建议：尽可能使用标准的 useEffect 以避免阻塞视觉更新，建议优先使用useEffect
 */
